热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

解析链接脚本与可执行文件的关系

本文详细探讨了链接脚本和可执行文件的基本概念及其在嵌入式系统中的应用,特别是S32K144芯片的具体实现。

本文由编程笔记小编整理,旨在介绍链接脚本和可执行文件的相关知识,帮助读者深入理解这些概念。


基本概念

以下是关于链接器、链接脚本和可执行程序的重要概念:

链接器(Linker)

链接器是一个程序,其主要功能是将多个目标文件(包括标准库函数的目标文件)的代码段、数据段及符号表等内容整合成一个符合ELF或EXE等格式的可执行二进制文件。

链接脚本

链接器在链接过程中需要使用链接脚本。如果没有通过“-T”参数指定链接脚本,链接器会使用默认的内部脚本。链接脚本的主要作用是将输入文件的段按指定的地址空间布局合并到输出文件的段中,使输出文件具备可执行性。

可执行程序

任何可执行程序,无论是Windows的.exe文件还是Linux的.elf文件,都由代码段、数据段、未初始化的数据段等部分组成。

段(Section)

每个输出段有两个地址:虚拟地址(Virtual Memory Address, VMA):运行时的地址;加载地址(Load Memory Address, LMA):加载时的地址。如果没有通过“AT”指定LMA,则LMA等于VMA。在嵌入式系统中,加载地址和运行地址常常不同,例如,程序可能存储在Flash中,但在运行时复制到RAM中。


分析S32K144的工程

我们以S32K1xx_flash_debug.ld为例,解析其内存映射和链接脚本的细节。

1. 内存映射(Memory Map)是处理器的存储布局,描述了CPU指令集可以直接寻址的外部存储空间。这些存储器可以是Flash、NAND Flash、SRAM、DDR等。

/* Specify the memory areas */
MEMORY
  /* Flash */
  m_interrupts          (RX)  : ORIGIN = 0x00000000, LENGTH = 0x00000400
  m_flash_config        (RX)  : ORIGIN = 0x00000400, LENGTH = 0x00000010
  m_text                (RX)  : ORIGIN = 0x00000410, LENGTH = 0x0007FBF0
  /* SRAM_L */
  m_data                (RW)  : ORIGIN = 0x1FFF8000, LENGTH = 0x00008000
  /* SRAM_U */
  m_data_2              (RW)  : ORIGIN = 0x20000000, LENGTH = 0x00007000

2. 目标可执行文件由输入文件的各个段填充,具体的填充规则和地址定义在链接脚本中。

中断向量和OS的异常向量表存储在m_interrupts段中,该段位于地址空间的ORIGIN = 0x00000000, LENGTH = 0x00000400。

.interrupts :
  
  __VECTOR_TABLE = .;
  . = ALIGN(4);
  "*(.isr_vector)"
  "*(Os_ExceptionVectors)" /* Startup code */
  . = ALIGN(4);
  > m_interrupts

代码段(.code)的虚拟地址(运行时地址)位于m_text (RX) : ORIGIN = 0x00000410, LENGTH = 0x0007FBF0,数据段的虚拟地址(运行时地址)位于m_data,bss段的虚拟地址(运行时地址)位于m_data_2。注意,段的虚拟地址和存储地址是不同的。

3. 链接脚本中的地址信息会被程序引用,因此链接脚本也是程序的一部分,可以看作是一个头文件。

__etext = .;    /* Define a global symbol at end of code. */
  __DATA_ROM = .; /* Symbol is used by startup for data initialization. */
  .data : AT(__DATA_ROM)
  
  . = ALIGN(4);
  __DATA_RAM = .;
  __data_start__ = .;      /* Create a global symbol at data start. */
  "*(.data)"               /* .data sections */
  "*(.data*)"              /* .data* sections */
  "*(.os_data)"
  "*(.mcal_data)"
  "*(.jcr*)"
  . = ALIGN(4);
  __data_end__ = .;        /* Define a global symbol at data end. */
  > m_data

虚拟地址是程序运行时的地址,加载地址是程序存储时的地址或加载源地址。

数据段定义了两个宏地址,这些宏会被C代码引用。

__DATA_ROM是紧接在代码段之后的一个地址,AT(__DATA_ROM)表示将.data段的加载地址设置为__DATA_ROM。

在startup_init_bss.c中引用了这个地址:

void init_data_bss(void)
data_rom       = (uint8_t *)__DATA_ROM;
/* Copy initialized data from ROM to RAM */
    while (data_rom_end != data_rom)
    
        *data_ram = *data_rom;
        data_ram++;
        data_rom++;
    

在startup.s中使用init_data_bss,将.data段从加载地址拷贝到运行地址(实际上是将数据从Flash拷贝到SRAM)。即使不拷贝,只要CPU可以寻址,程序也能执行,因为Flash和SRAM都在地址空间内。

; Init .data and .bss sections
    LDR     R0,=init_data_bss
    BLX     R0
    ;cpsie   i              ; Unmask interrupts
    BL      main

从规格书中查找memory map:

总结:

1. 可执行程序由数据段、代码段、bss段等组成。

2. 每个段对应两个地址:虚拟地址(运行地址)和加载地址(存储地址),这些地址都在CPU可寻址的地址空间内。

3. 链接脚本中的地址信息会被C代码引用,链接脚本可以视为程序的一部分(类似头文件)。

4. CPU的memory map对应的存储器可以是Flash、SRAM、DDR等。如果加载地址和运行地址不同,程序在启动时(如在startup.s中)需要将加载地址的内容复制到运行地址。加载地址和运行地址由程序员根据存储器特性自定义。


推荐阅读
  • 本文将介绍如何编写一些有趣的VBScript脚本,这些脚本可以在朋友之间进行无害的恶作剧。通过简单的代码示例,帮助您了解VBScript的基本语法和功能。 ... [详细]
  • Python 异步编程:深入理解 asyncio 库(上)
    本文介绍了 Python 3.4 版本引入的标准库 asyncio,该库为异步 IO 提供了强大的支持。我们将探讨为什么需要 asyncio,以及它如何简化并发编程的复杂性,并详细介绍其核心概念和使用方法。 ... [详细]
  • 技术分享:从动态网站提取站点密钥的解决方案
    本文探讨了如何从动态网站中提取站点密钥,特别是针对验证码(reCAPTCHA)的处理方法。通过结合Selenium和requests库,提供了详细的代码示例和优化建议。 ... [详细]
  • 本文详细介绍了Akka中的BackoffSupervisor机制,探讨其在处理持久化失败和Actor重启时的应用。通过具体示例,展示了如何配置和使用BackoffSupervisor以实现更细粒度的异常处理。 ... [详细]
  • Android 渐变圆环加载控件实现
    本文介绍了如何在 Android 中创建一个自定义的渐变圆环加载控件,该控件已在多个知名应用中使用。我们将详细探讨其工作原理和实现方法。 ... [详细]
  • DNN Community 和 Professional 版本的主要差异
    本文详细解析了 DotNetNuke (DNN) 的两种主要版本:Community 和 Professional。通过对比两者的功能和附加组件,帮助用户选择最适合其需求的版本。 ... [详细]
  • golang常用库:配置文件解析库/管理工具viper使用
    golang常用库:配置文件解析库管理工具-viper使用-一、viper简介viper配置管理解析库,是由大神SteveFrancia开发,他在google领导着golang的 ... [详细]
  • 深入解析JVM垃圾收集器
    本文基于《深入理解Java虚拟机:JVM高级特性与最佳实践》第二版,详细探讨了JVM中不同类型的垃圾收集器及其工作原理。通过介绍各种垃圾收集器的特性和应用场景,帮助读者更好地理解和优化JVM内存管理。 ... [详细]
  • 深入解析Android自定义View面试题
    本文探讨了Android Launcher开发中自定义View的重要性,并通过一道经典的面试题,帮助开发者更好地理解自定义View的实现细节。文章不仅涵盖了基础知识,还提供了实际操作建议。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 本文基于刘洪波老师的《英文词根词缀精讲》,深入探讨了多个重要词根词缀的起源及其相关词汇,帮助读者更好地理解和记忆英语单词。 ... [详细]
  • 深入理解 SQL 视图、存储过程与事务
    本文详细介绍了SQL中的视图、存储过程和事务的概念及应用。视图为用户提供了一种灵活的数据查询方式,存储过程则封装了复杂的SQL逻辑,而事务确保了数据库操作的完整性和一致性。 ... [详细]
  • 深入理解Java中的volatile、内存屏障与CPU指令
    本文详细探讨了Java中volatile关键字的作用机制,以及其与内存屏障和CPU指令之间的关系。通过具体示例和专业解析,帮助读者更好地理解多线程编程中的同步问题。 ... [详细]
  • 在前两篇文章中,我们探讨了 ControllerDescriptor 和 ActionDescriptor 这两个描述对象,分别对应控制器和操作方法。本文将基于 MVC3 源码进一步分析 ParameterDescriptor,即用于描述 Action 方法参数的对象,并详细介绍其工作原理。 ... [详细]
  • 本文详细介绍了如何在Ubuntu系统中下载适用于Intel处理器的64位版本,涵盖了不同Linux发行版对64位架构的不同命名方式,并提供了具体的下载链接和步骤。 ... [详细]
author-avatar
UNESCO媒介与女性教席走_890
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有